package tt.dz.order.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 
 * @name 线程安全的日志类型转换
 * @author Anson
 * @version 1.0
 * @summary 通过ThreadLocal解决线程安全问题
 * @desc <strong>原因</strong>:<code>SimpleDateFormat</code>
 *       (下面简称sdf)类内部有一个Calendar对象引用,<br>
 *       它用来储存和这个sdf相关的日期信息,例如<code>sdf.parse(dateStr)</code>,
 *       <code>sdf.format(date)</code> 诸如此类的方法参数传入的日期相关String, Date等等, <br>
 *       都是交友<code>Calendar</code>引用来储存的.这样就会导致一个问题,如果你的sdf是个static的, <br>
 *       那么多个thread 之间就会共享这个sdf, 同时也是共享这个<code>Calendar</code>引用<br>
 *       则会导致在多线程下日期的相关转换，出现线程安全问题<br>
 *       当前为了解决当前问题，则会进行线程安全处理
 *
 */
public final class ThreadSafeDateFormat {
	/** 锁对象 */
	private static final Lock lock = new ReentrantLock();

	/** 存放不同的日期模板格式的sdf的Map */
	private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new ConcurrentHashMap<String, ThreadLocal<SimpleDateFormat>>();
	/**
	 * 时间格式化
	 */
	public static final String TIME_FMT = "yyyy-MM-dd HH:mm:ss";

	/**
	 * 日期格式化
	 */
	public static final String DATE_FMT = "yyyy-MM-dd";

	/**
	 * 年格式化
	 */
	public static final String YEAR_FMT = "yyyy";

	/**
	 * 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
	 * 
	 * @author Anson
	 * @createDate 2016年4月27日
	 * @desc 通过LocalThread处理线程安全处理
	 * @param pattern
	 *            格式字符
	 * @see ThreadLocal#initialValue()
	 * @return SimpleDateFormat
	 */
	private static SimpleDateFormat getSdf(final String pattern) {
		ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);
		// 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
		if (tl == null) {
			lock.lock();
			try {
				tl = sdfMap.get(pattern);
				if (tl == null) {
					// 使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
					tl = new ThreadLocal<SimpleDateFormat>() {
						@Override
						protected SimpleDateFormat initialValue() {
							return new SimpleDateFormat(pattern);
						}
					};
					sdfMap.put(pattern, tl);
				}

			} finally {
				lock.unlock();
			}
		}

		return tl.get();
	}

	/**
	 * 
	 * 格式化当前日期为字符串
	 * 
	 * @author Anson
	 * @createDate 2016年4月27日
	 * @desc 是用ThreadLocal<br>
	 *       来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
	 * @param date
	 *            日期
	 * @param pattern
	 *            格式
	 * @return 转换之后的格式
	 */
	public static String format(Date date, String pattern) {
		return getSdf(pattern).format(date);
	}

	/**
	 * 将字符串转换成一个日期类型
	 * 
	 * @author Anson
	 * @createDate 2016年4月27日
	 * @desc 是用ThreadLocal<br>
	 *       来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
	 * @param dateStr
	 *            日期字符串
	 * @param pattern
	 *            格式
	 * @return Date
	 * @throws ParseException
	 */
	public static Date parse(String dateStr, String pattern) throws ParseException {
		return getSdf(pattern).parse(dateStr);
	}

}
